﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Media.Media3D;
using System.Windows;

namespace SosaAnalysisModule
{
    public class Peg
    {
        private const double HEIGHTOFFSET = 0.5;
        private const double TEXTSIZE = .25;
        private const double TEXTLENGTH = .5;
        private const double DEFAULTSIZE = .25;
        private const double DEFAULTHEIGHT = 1.25;

        protected Viewport3D Viewport;
        protected ModelVisual3D PegModel;
        protected ModelVisual3D TextLabelModel;
        protected Color TextColor = Colors.DarkGray;
        protected Color PegColor = Colors.Blue;
        protected String Label;

        protected double X;
        protected double Y;

        public Peg(Viewport3D view)
        {
            Viewport = view;
        }

        /// <summary>
        /// Adds the peg to the board data
        /// </summary>
        /// <returns></returns>
        private Model3DGroup CreateTriangleModel(Point3D p0, Point3D p1, Point3D p2, Color color)
        {
            MeshGeometry3D mesh = new MeshGeometry3D();
            mesh.Positions.Add(p0);
            mesh.Positions.Add(p1);
            mesh.Positions.Add(p2);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(1);
            mesh.TriangleIndices.Add(2);
            Vector3D normal = CalculateNormal(p0, p1, p2);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            mesh.Normals.Add(normal);
            Material material = new DiffuseMaterial(new SolidColorBrush(color));
            GeometryModel3D model = new GeometryModel3D(mesh, material);
            Model3DGroup group = new Model3DGroup();
            group.Children.Add(model);
            return group;
        }

        /// <summary>
        /// Calculates the normal of a triangle
        /// Paramters : the three points of a 3D triangle
        /// Return: The normal of the triangle
        /// </summary>
        /// <param name="p0"></param>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        private Vector3D CalculateNormal(Point3D p0, Point3D p1, Point3D p2)
        {
            Vector3D v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
            Vector3D v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);

            return Vector3D.CrossProduct(v0, v1);
        }

        /// <summary>
        /// Draws the peg mesh to the board
        /// </summary>
        /// <param name="label"></param>
        /// <param name="xP"></param>
        /// <param name="yP"></param>
        public void createPeg(String label, double xP, double yP)
        {
            Model3DGroup pyr = new Model3DGroup();

            Point3D p0 = new Point3D(xP - DEFAULTSIZE, DEFAULTHEIGHT, yP - DEFAULTSIZE);
            Point3D p1 = new Point3D(xP + DEFAULTSIZE, DEFAULTHEIGHT, yP - DEFAULTSIZE);
            Point3D p2 = new Point3D(xP - DEFAULTSIZE, DEFAULTHEIGHT, yP + DEFAULTSIZE);
            Point3D p3 = new Point3D(xP + DEFAULTSIZE, DEFAULTHEIGHT, yP + DEFAULTSIZE);
            Point3D p4 = new Point3D(xP, 0, yP);

            pyr.Children.Add(CreateTriangleModel(p3, p0, p2, PegColor));
            pyr.Children.Add(CreateTriangleModel(p1, p0, p3, PegColor));

            pyr.Children.Add(CreateTriangleModel(p1, p3, p4, PegColor));
            pyr.Children.Add(CreateTriangleModel(p4, p3, p2, PegColor));
            pyr.Children.Add(CreateTriangleModel(p0, p4, p2, PegColor));
            pyr.Children.Add(CreateTriangleModel(p1, p4, p0, PegColor));

            X = xP;
            Y = yP;
            Label = label;


            PegModel = new ModelVisual3D();
            PegModel.Content = pyr;
            PegModel.Transform = new Transform3DGroup();

            CreateTextLabel3D(Label, true);

            Viewport.Children.Add(PegModel);
        }

        /// <summary>
        /// Draws the label text above the peg
        /// </summary>
        /// <param name="label"></param>
        /// <param name="bDoubleSided"></param>
        public void CreateTextLabel3D(string label, bool bDoubleSided)
        {

            //Create a new text block based on the label's text
            //Then set the text's color and its font style
            TextBlock textBlock = new TextBlock(new Run(label));
            textBlock.Foreground = new SolidColorBrush(TextColor);
            textBlock.FontFamily = new FontFamily("Arial");

            //Create a new new martial for the text to be displayed on
            DiffuseMaterial material = new DiffuseMaterial();
            material.Brush = new VisualBrush(textBlock);

            // Since the parameter coming in was the center of the label,
            // we need to find the four corners
            // p0 is the lower left corner
            // p1 is the upper left
            // p2 is the lower right
            // p3 is the upper right
            Point3D p0 = new Point3D(X, DEFAULTHEIGHT + HEIGHTOFFSET, Y + TEXTLENGTH);
            Point3D p1 = new Point3D(X, DEFAULTHEIGHT + HEIGHTOFFSET + TEXTSIZE, Y + TEXTLENGTH);
            Point3D p2 = new Point3D(X, DEFAULTHEIGHT + HEIGHTOFFSET, Y - TEXTLENGTH);
            Point3D p3 = new Point3D(X, DEFAULTHEIGHT + HEIGHTOFFSET + TEXTSIZE, Y - TEXTLENGTH);

            // Now build the geometry for the sign.  It's just a
            // rectangle made of two triangles, on each side.

            MeshGeometry3D mesh = new MeshGeometry3D();
            mesh.Positions = new Point3DCollection();
            mesh.Positions.Add(p0);
            mesh.Positions.Add(p1);
            mesh.Positions.Add(p2);
            mesh.Positions.Add(p3);

            if (bDoubleSided)
            {
                mesh.Positions.Add(p0);
                mesh.Positions.Add(p1);
                mesh.Positions.Add(p2);
                mesh.Positions.Add(p3);
            }

            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(3);
            mesh.TriangleIndices.Add(1);
            mesh.TriangleIndices.Add(0);
            mesh.TriangleIndices.Add(2);
            mesh.TriangleIndices.Add(3);

            if (bDoubleSided)
            {
                mesh.TriangleIndices.Add(4);
                mesh.TriangleIndices.Add(5);
                mesh.TriangleIndices.Add(7);
                mesh.TriangleIndices.Add(4);
                mesh.TriangleIndices.Add(7);
                mesh.TriangleIndices.Add(6);
            }

            // These texture coordinates basically stretch the
            // TextBox brush to cover the full side of the label.

            mesh.TextureCoordinates.Add(new Point(0, 1));
            mesh.TextureCoordinates.Add(new Point(0, 0));
            mesh.TextureCoordinates.Add(new Point(1, 1));
            mesh.TextureCoordinates.Add(new Point(1, 0));

            if (bDoubleSided)
            {
                mesh.TextureCoordinates.Add(new Point(1, 1));
                mesh.TextureCoordinates.Add(new Point(1, 0));
                mesh.TextureCoordinates.Add(new Point(0, 1));
                mesh.TextureCoordinates.Add(new Point(0, 0));
            }

            // And that's all.  Return the result.

            TextLabelModel = new ModelVisual3D();
            TextLabelModel.Content = new GeometryModel3D(mesh, material);
            // TextLabelModel.Transform = new Transform3DGroup();

            PegModel.Children.Add(TextLabelModel);
        }

        #region Set

        public void setViewport(Viewport3D v)
        {
            Viewport = v;
        }

        public void setPegModel(ModelVisual3D m)
        {
            PegModel = m;
        }

        public void setX(string s)
        {
            Label = s;
        }

        public void setX(double d)
        {
            X = d;
        }
        public void setY(double d)
        {
            Y = d;
        }

        #endregion

        #region Get

        public Viewport3D getViewPort()
        {
            return Viewport;
        }

        public ModelVisual3D getPegModel()
        {
            return PegModel;
        }

        public ModelVisual3D getTextLabelModel()
        {
            return TextLabelModel;
        }

        public string getLabel()
        {
            return Label;
        }

        public double getX()
        {
            return X;
        }

        public double getY()
        {
            return Y;
        }

        #endregion
    }
}
